
#ifndef __3d_terrainH__
#define __3d_terrainH__

#include "3d_material.h"
#include "heightmap.h"

namespace WtfGraphics {
	/// The tileset facilitiates the assigment of materials and types to points
	/// in the terrain.
	class Tileset: public WtfEngine::DataObject {
		DATA_LOADABLE(Tileset, GC_ADDEDSIZE_AUTO(WtfEngine::DataObject, Tileset));
	public:
		/// Assignment of a tile material to a point
		struct TileAssignment {
			Material::tCacheRef rTileMaterial;
			tScalar fOpacity;

			inline TileAssignment(const Material::tCacheRef& rMat, const tScalar opac)
				: rTileMaterial(rMat), fOpacity(opac) {};
		};

		typedef std::vector<TileAssignment> tTiling;

		//typedef std::map<WtfEngine::StringPool::Entry, 
	private:
		Tileset::tCacheRef mrParent;

	public:
		Tileset(const WtfEngine::StringPool::Entry& sName): WtfEngine::DataObject(sName) {};

		/// Calculate the tiling of a point based upon its position and slope (normal)
		void CalculateTiling(const tPosition& vPos, const tDirection& vNorm, tTiling& vTiles);

		static WtfEngine::IDataObject * Load(const WtfEngine::StringPool::Entry& sName);
	};

	class Terrain: public GraphicsData {
	private:
		
		/// Assignment of a tile material to a triangle
		struct TileAssignment {
			Material::tCacheRef rTileMaterial;
			tVector vOpacity;

			inline TileAssignment(const Material::tCacheRef& rMat, const tVector& vOpac = tVector())
				: rTileMaterial(rMat), vOpacity(vOpac) {};

			inline bool operator == (const TileAssignment& v) const {
				return (v.rTileMaterial == rTileMaterial);
			};
		};
		
		/// Support multiple blended tiles per triangle
		typedef std::vector<TileAssignment> tTiling;

		/// Stores data for generation/modification of segments
		struct SegmentCreationData {
			WtfEngine::Heightmap::tCacheRef rHeightmap;
			Tileset::tCacheRef rTileset;
			tVector vScale;
			tVector vSize;
			
			/// Triangles stored in a tree structure, with each level
			/// of tree stored in its own array. This allows easy creation of
			/// VBOs for certain levels of detail.
			std::vector<std::vector<Triangle> > vvTriangle;

			/// Vertex normals
			std::vector<std::vector<Triangle> > vvNormal;

			/// Per-vertex tiling assignments
			std::vector<std::vector<Tileset::tTiling> > vvVertexTiling;
			/// Per-triangle tiling assignments
			std::vector<std::vector<tTiling> > vvTiling;

			SegmentCreationData(const WtfEngine::Heightmap::tRef& rHm, const Tileset::tCacheRef& rTileset,
				const tVector& vSize, int iLevels);
			
			void InitVertex(int iLevel, int iPos, int iVert, const tPosition& vPos);
			void CopyVertex(int iLevel, int iPos, int iVert, int iParentVert);

			void CreateTriangle(int iLevel, int iPos, int nSubs);
		};

		/// A segment of the terrain
		struct Segment {
			Mesh::tRef rMesh;
			Material::tRef rMaterial;
		};

		/// Array of terrain segments for each LOD.
		std::vector<std::vector<Segment>> mvvSegment;

	public:
		Terrain(const WtfEngine::StringPool::Entry& sName);

		/// Read terrain from file. Should actually be a folder containing
		/// the heightmap, typemap and texture mapping rules
		void Read(const WtfEngine::String& sName);
		void GenerateMaps(int iLevel, int iSeg, SegmentCreationData * pData);

		void render();

		static WtfEngine::IDataObject * Load(const WtfEngine::StringPool::Entry& sName);
		DATA_LOADABLE(Terrain, GC_ADDEDSIZE_AUTO(GraphicsData, Terrain));
	};

	void TestTerrain();
};

#endif
